import { db, lib, callCloud } from "./common/index";
import { getUser } from "./user"
const _ = db.command
const _cloudStorage = {}


// const dbkeys = ['_id', 'UID', '__action__', '__collection__', '__datakey__', '__value__', '__web_uid__', 'user']
const dbkeys = ['_id', 'UID', '__action__', '__collection__', '__datakey__', '__value__', '__web_uid__']

/**
 * 
 * @param {Object} res 通过ajax获取的远程数据
 * @param {String|Array|Object} param 指定需要返回的数据，默认仅返回value
 * @param {Function} cb 执行客户端回调方法
 */
function returnValue(res, param, cb){
  const profile = {}
  const attributts = {}
  const post = (res.result.data && res.result.data[0]) || res.result.data || res.result
  Object.keys(post).forEach( ky=> dbkeys.indexOf(ky) > -1 ? attributts[ky] = post[ky] : profile[ky] = post[ky])

  if (post) {
    const tmp = {
      _id: attributts._id,
      _key: attributts.__datakey__,
      data: attributts.__value__
    }
    let resultPart = ''
    if (lib.isObject(param)) {
      resultPart = param.result
    } else if (lib.isString(param)) {
      resultPart = param
    } else if (lib.isArray(param)) {
      resultPart = param
    }
    if (resultPart) {
      resultPart = [].concat(resultPart)
      resultPart.forEach(ky=> tmp[ky] = profile[ky])
    }
    if (lib.isFunction(cb)) cb(tmp)
    return tmp
  }
}

function watchCollection(collectionName, user, cb) {
  let UID = user.UID
  let that = this
  let watch_where = Object.assign({}, {UID}, this._watch_where)

  this.watcher = db.collection(collectionName)
    // 按 progress 降序
    .orderBy('progress', 'desc')
    // .limit(10)
    .where(watch_where)
    .watch({
      onChange: function (snapshot) {
        if (lib.isFunction(cb)) {
          cb(snapshot)
        }
        that.hooks.emit('watch', snapshot)
      },
      onError: function (err) {
        console.error('the watch closed because of error', err)
      }
    })
}

function createCollection(){
  callCloud(this.baseUrl + '/create').then(res => {
    this.collection = db.collection(this.collectionName)  // 需要在云端配置中设置权限
  })
}

class myStorage {
  constructor(collectionName, cb){
    this.collectionName = collectionName
    this.baseUrl = 'storage/'+collectionName
    this.hooks = lib.hooks(this.baseUrl, {max: 100}) || null

    if (typeof wx === 'undefined') { // pc版需要开启云端匿名登录支持
      this.env = {}
    } else {
      this.env = wx.isCloud
    }
    getUser.call(this, (ret)=>{
      this.user = ret
      this.hooks.emit('get_user')
      createCollection.call(this)
    })
  }
  count(){}
  watch(k, param, cb){
    this._watch_where = {}
    if (lib.isFunction(k)) {
      cb = k
      k = null
    }

    if (lib.isFunction(param)) {
      cb = param
      param = null
    }

    if (lib.isObject(k)) {
      param = k
      k = null
    }
    if (lib.isString(k) && k.indexOf('.')>-1) {
      console.warn('不支持监控子属性');
      return
    }
    if (lib.isString(k) && !param) {
      param = k
    }
    if (lib.isString(param) || lib.isString(k)) {
      param = {__datakey__: (k||param)}
    }
    if (lib.isObject(param)) {
      this._watch_where = param
    }
    if (this.user) {
      watchCollection.call(this, this.collectionName, this.user, cb)
    } else {
      this.hooks.one('get_user', ()=>{
        watchCollection.call(this, this.collectionName, this.user, cb)
      })
    }
  }
  
  async setItem(k, v, expire){
    let tmp = {}
    if (v._id) { delete v._id }
    tmp.expire = expire
    tmp.__value__ = v

    if (lib.isString(k)) {
      let filds = []
      if (k.indexOf('.') > -1) {
        const keys = k.split('.')
        k = keys[0]
        filds = keys.splice(1, keys.length)
      }

      if (filds.length) {
        let result = await this.getItem(k)
        if (result) {
          let data = result.data
          if (lib.isString(data)) data = v
          else if (typeof data === 'object') {
            data = filds.reduce((p, n)=>{
              if (filds.indexOf(n) === (filds.length-1)) {
                p[n] = v
                return data
              } else {
                if (!p[n]) p[n] = {}
                return p[n]
              }
            }, data)  
            tmp.__value__ = data
          }
        }
      }

      return callCloud(this.baseUrl + '/setItem/'+k, tmp)
    }
  }

  // 获取当前小程序用户 instance.getUser().then(res=>{})
  // 允许获取非当前的用户信息 instance.getUser({UID: 'xxx'}).then(res=>{})
  getUser(param, cb){ 
    return getUser.call(this, param, cb)
  }

  async getItem(k, param={}, cb){
    if (lib.isObject(k)) {
      param = k
      k = 'getByParam'
    }

    if (lib.isFunction(param)) {
      cb = param
      param = null
    }

    if (lib.isString(k)) {
      let result = null
      let filds = []
      if (k.indexOf('.') > -1) {
        const keys = k.split('.')
        k = keys[0]
        filds = keys.splice(1, keys.length)
      }

      if (filds.length) {
        result = await this.getItem(k)
        if (result) {
          let data = result.data
          if (!lib.isString(data)) {
            data = filds.reduce((p, n)=>{
              if (!p) return null
              if (filds.indexOf(n) === (filds.length-1)) return p[n]
              else {
                return p[n]
              }
            }, data)
          }
          if (lib.isFunction(cb)) cb(data)
          return data
        }
      } else {
        return new Promise((resolve, rej)=>{
          callCloud(this.baseUrl + '/getItem/' + k).then(res => {
            resolve(returnValue(res, param, cb))
          })
        })
      }
    }
  }

  async appendItem(k, pay, expire){
    return await this.updateItem(k, pay, expire)
  }

  async updateItem(k, pay, expire){
    if (lib.isString(k)) {
      let filds = []
      if (lib.isString(k) && k.indexOf('.') > -1) {
        const keys = k.split('.')
        k = keys[0]
        filds = keys.splice(1, keys.length)
      }

      let result = await this.getItem(k)
      if (result) {
        let data = result.data || pay
        if (data && pay) {
          if (filds.length) {
            if (lib.isString(data)) data = {[filds[0]]: data}
            if (typeof data === 'object') {
              data = filds.reduce((p, n)=>{
                if (filds.indexOf(n) === (filds.length-1)) {
                  p[n] = pay
                  return data
                } else {
                  if (!p[n]) p[n] = {}
                  return p[n]
                }
              }, data)
            }
          } else {
            if (lib.isArray(data)) {
              data = data.concat(pay)
            } else if (lib.isObject(data) && lib.isObject(pay)) {
              data = Object.assign({}, data, pay)
            } else if (lib.isString(data)) {
              data = [data, pay]
            }
          }
        }
        return await this.setItem(k, data, expire)
      } else {
        if (pay) {
          return await this.setItem(k, pay, expire)
        }
      }
    }
  }
  
  async removeItem(k, cb){
    if (lib.isString(k)) {
      let result = '__empty__'
      let filds = []
      if (k.indexOf('.') > -1) {
        const keys = k.split('.')
        k = keys[0]
        filds = keys.splice(1, keys.length)
        result = await this.getItem(k)
        if (result) {
          let data = result.data
          if (filds.length) {
            if (lib.isString(data)) data = null
            if (data && typeof data === 'object') {
              data = filds.reduce((p, n)=>{
                if (!p) return null 
                if (filds.indexOf(n) === (filds.length-1)) {
                  if (lib.isArray(p)) {
                    const _n = parseInt(n)
                    _n ? p.splice(_n, 1) : p[n] = null
                  } else {
                    p[n] = null
                  }
                  return data
                } else {
                  return p[n]
                }
              }, data)
            }
          }
          result.data = data
        }
      }
      if (result!=='__empty__') {
        await this.setItem(k, result.data)
        return {success: true}
      } else {
        return new Promise((resolve, reject)=>{
          if (typeof cb === 'object') {
            let tmp = {byId: true}
            let param = cb
            if (lib.isArray(param)) {
              tmp.id = param
              param = tmp
            }
            if (lib.isObject(param)) {
              param = Object.assign(param, tmp)
            }

            if (param.id) {
              param.id = [].concat(param.id)
              callCloud(this.baseUrl + '/removeItem/' + k, param).then(res => {
                resolve({success: true})
              })
            }

          } else {
            callCloud(this.baseUrl + '/removeItem/' + k).then(res => {
              console.log(res);
              if (lib.isFunction(cb)) cb({success: true})
              resolve({success: true})
            })
          }
        })
      }
    }
  }

  clear(){
    callCloud(this.baseUrl + '/clear').then(res => {
      // console.log('====== clear', res);
    })
  }

  destory(){
    this.watcher.close()
    _cloudStorage[this.collectionName] = null
  }


  doc = {
    // .doc
    // .where
    // .group
    // .limit
    list: async (uid, param={}, cb) => {
      if (typeof param === 'function') {
        cb = param
        param={}
      }
      
      if (lib.isObject(uid)) {
        param = uid
        uid = null
      }
      
      const userId = uid ? uid : 'administrator'
      if (userId === 'administrator') {
        param['__web_uid__'] = 'administrator'
      }
      
      return new Promise((resolve, rej)=>{
        const ret = []
        callCloud(this.baseUrl + '/getList/'+userId, param).then(res=>{
          let result = res.result.data || res.result
          if (result.length) { 
            result.forEach(post=>{
              ret.push(returnValue({result: post}, param))
            })
          }
          if (lib.isFunction(cb)) cb({data: ret})
          resolve({data: ret})
        })
      })
    },

    add: async (pay, expire, cb)=>{
      const key = lib.uuid(this.collectionName+'-', 12)
      await this.setItem(key, pay, expire)
      if (lib.isFunction(cb)) cb()
    },

    doc: async (id, param, cb) => {
      if (typeof param === 'function') {
        cb = param
        param={}
      }
      if (lib.isString(id)) {
        return new Promise((resolve, reject)=>{
          callCloud(this.baseUrl + '/getItem/getDocmentById', {byId: true, id}).then(res=>{
            resolve(returnValue(res, param, cb))
          })
        })
      }
    },

    update: async (id, pay, expire, cb) => {
      let tmp = {}

      if (lib.isFunction(expire)) {
        cb = expire
        expire = null
      }

      tmp.expire = expire
      tmp.__value__ = pay
      tmp.byId = true
      tmp.id = id
      const res = await callCloud(this.baseUrl + '/updateItem/'+id, tmp)
      if (lib.isFunction(cb)) cb()
      return res
    },

    set: async (id, pay, expire, cb) => {
      let tmp = {}

      if (lib.isFunction(expire)) {
        cb = expire
        expire = null
      }

      tmp.expire = expire
      tmp.__value__ = pay
      tmp.byId = true
      tmp.id = id
      const res = callCloud(this.baseUrl + '/setItem/'+id, tmp)
      if (lib.isFunction(cb)) cb()
      return res
    },

    async get(id, param, cb){
      return this.doc(id, param, cb)
    },

    delete: async (id)=>{
      if (id) {
        id = [].concat(id)
        return callCloud(this.baseUrl + '/removeItem/deleteById', {byId: true, id})
      }
    },
  }
}

// 所有集合的集合
export async function manageDBtable(tableName, param={}){
  if (lib.isString(param)) {
    param.title = param
  }
  const manager =  new myStorage('CLOUD-TABLE-MANAGER', async function(context){
    if (tableName) {
      const exits = await manager.doc.list({where: {tableName} })
      if (!exits.data.length) {
        manager.doc.add({tableName, title: param.title})
      }
    }
  })

  // 停用某一个集合
  manager.switch = async function(id){
    const res = await manager.doc.doc(id)
    if (res) {
      if (res.switch) {
        manager.doc.update(id, {switch: false})
        return false
      }
      else {
        manager.doc.update(id, {switch: true})
        return true
      }
    }
  }
  
  
  // 删除集合
  // 并不能真正删除集合，需要手动在云平台删除相关的集合，该功能仅仅删除CLOUD-TABLE-MANAGER表中的数据
  const oldRemoveItem = manager.removeItem
  manager.removeItem = async function(){
    return oldRemoveItem.apply(manager, arguments)
  }

  // 根据id删除集合 
  // 并不能真正删除集合，需要手动在云平台删除相关的集合，该功能仅仅删除CLOUD-TABLE-MANAGER表中的数据
  const oldDocDelete = manager.doc.delete
  manager.doc.delete = async function(){
    return oldDocDelete.apply(manager, arguments)
  }
  
  return manager
}


// const cloudScore = require('./common/cloud')('score')   // 创建一个集合
export function cloudStorage(collectionName, param={}, cb) {
  if (collectionName){
    const paramForManager = {}
    if (lib.isFunction(param)) {
      cb = param
      param = {}
    }
    paramForManager.title = param.title
    if (!_cloudStorage[collectionName]){
      _cloudStorage[collectionName] = new myStorage(collectionName, param)
      manageDBtable(collectionName, paramForManager)
    }
    return _cloudStorage[collectionName]
  }
}
